package web

import (
	"gitee.com/Hu-Lyndon/distill-resk/infra"
	"gitee.com/Hu-Lyndon/distill-resk/infra/authentication/middleware"
	"gitee.com/Hu-Lyndon/distill-resk/infra/utils"
	"gitee.com/Hu-Lyndon/distill-resk/infra/utils/supports"
	"github.com/tietang/props/kvs"
	"strings"

	//"gitee.com/Hu-Lyndon/distill-resk/infra/web/middleware"
	"github.com/iris-contrib/middleware/cors"
	"github.com/kataras/iris"
	"github.com/kataras/iris/context"
	"github.com/kataras/iris/middleware/logger"
	irisrecover "github.com/kataras/iris/middleware/recover"
	log "github.com/sirupsen/logrus"
	"time"
)

var (
	irisApplication *iris.Application
	main            iris.Party
)

func irisApp() *iris.Application {
	utils.Check(irisApplication)
	return irisApplication
}

func IrisMainParty() iris.Party {
	return main
}

type IrisServerStarter struct {
	infra.BaseStarter
}

func (i *IrisServerStarter) Init(ctx infra.StarterContext) {

	var err error
	conf := ctx.Props()

	webConfig := IrisConfig{}
	err = kvs.Unmarshal(conf, &webConfig, "web.irisconfig")
	if err != nil {
		log.Fatalf("配置IrisConfig失败: %s", err)
	}

	other := Other{}
	err = kvs.Unmarshal(conf, &other, "web.other")
	if err != nil {
		log.Fatalf("配置IrisOther失败: %s", err)
	}

	irisConfig := webConfig.ConvertIrisConfig()
	//irisConfig.Other, err = other.ConvertOtherToMap() // 看来还是不如toml
	otherToMap, err := other.ConvertOtherToMap()
	if err != nil {
		log.Fatalf("IrisOther转换MAP失败: %s", err)
	}

	var otherTargetMap = make(map[string]interface{})

	for okey, ovalue := range otherToMap {
		if okey == "IgnoreURLs" {
			iglist := strings.Split(ovalue.(string), ",")
			otherTargetMap[okey] = iglist
		} else {
			otherTargetMap[okey] = ovalue
		}
	}

	irisConfig.Other = otherTargetMap

	//创建iris application实例
	irisApplication = initIris(irisConfig)
	//日志组件配置和扩展，这里采用logrus的标准日志策略
	logger := irisApplication.Logger()
	logger.Install(log.StandardLogger())
	main = corsSetting(irisApplication)
}

func (i *IrisServerStarter) Setup(ctx infra.StarterContext) {

}
func (i *IrisServerStarter) Start(ctx infra.StarterContext) {
	//和logrus日志级别保持一致
	irisApp().Logger().SetLevel(ctx.Props().GetDefault("log.level", "info"))

	//把路由信息打印到控制台
	log.Info("OUT Routes")
	routes := irisApp().GetRoutes()
	for _, r := range routes {
		log.Info(r.Trace())
	}
	log.Info("DOWN OUT Routes")
	//启动iris
	port := ctx.Props().GetDefault("app.server.port", "18080")
	irisApp().Run(iris.Addr(":" + port))
}
func (i *IrisServerStarter) StartBlocking() bool {
	return true
}

func initIris(iconf iris.Configuration) *iris.Application {
	app := iris.New()
	app.Configure(iris.WithConfiguration(iconf))
	app.Use(irisrecover.New())
	// 主要中间件的配置:recover,日志输出中间件的自定义
	cfg := logger.Config{
		Status:             true,
		IP:                 true,
		Method:             true,
		Path:               true,
		Query:              true,
		Columns:            true,
		MessageContextKeys: []string{"logger_message"},
		MessageHeaderKeys:  []string{"User-Agent"},
		LogFunc: func(now time.Time, latency time.Duration,
			status, ip, method, path string,
			message interface{},
			headerMessage interface{}) {
			app.Logger().Infof("| %s | %s | %s | %s | %s | %s | %+v | %+v",
				now.Format("2006-01-02.15:04:05.000000"),
				latency.String(), status, ip, method, path, headerMessage, message,
			)
		},
	}
	app.Use(logger.New(cfg))

	//app.Configure(iris.WithConfiguration())

	preSetting(app)

	return app
}

func preSetting(app *iris.Application) {
	//app.ScannerConf().SetLevel(conf.O.LogLevel)
	logLevelStr := app.ConfigurationReadOnly().GetOther()["LogLevel"].(string)
	app.Logger().SetLevel(logLevelStr)
	//app.ScannerConf().Handle(func(l *golog.Log) bool {
	//	prefix := golog.GetTextForLevel(l.Level, true)
	//
	//	filename, line := getCaller()
	//	message := fmt.Sprintf("%s %s [%s:%d] %s",
	//		prefix, l.FormatTime(), filename, line, l.Message)
	//
	//	if l.NewLine {
	//		message += "\n"
	//	}
	//
	//	fmt.Print(message)
	//	return true
	//})

	errLogger, close := supports.NewRequestLogger()
	defer close()
	app.Use(
		errLogger, // 记录请求
		//middleware.ServeHTTP
	)

	// ---------------------- 定义错误处理 ------------------------
	app.OnErrorCode(iris.StatusNotFound, errLogger, func(ctx iris.Context) {
		supports.Error(ctx, iris.StatusNotFound, supports.NotFound, nil)
	})
	app.OnErrorCode(iris.StatusInternalServerError, errLogger, func(ctx iris.Context) {
		supports.Error(ctx, iris.StatusInternalServerError, supports.StatusInternalServerError, nil)
	})
	//app.OnErrorCode(iris.StatusForbidden, customLogger, func(ctx iris.Context) {
	//	ctx.JSON(utils.Error(iris.StatusForbidden, "权限不足", nil))
	//})
	////捕获所有http错误:
	//app.OnAnyErrorCode(customLogger, func(ctx iris.Context) {
	//	//这应该被添加到日志中，因为`logger.Config＃MessageContextKey`
	//	ctx.Values().Set("logger_message", "a dynamic message passed to the logs")
	//	ctx.JSON(utils.Error(500, "服务器内部错误", nil))
	//})
}

func corsSetting(app *iris.Application) (main iris.Party) {
	var (
		crs context.Handler
	)

	crs = cors.New(cors.Options{
		AllowedOrigins: []string{"*"}, //允许通过的主机名称
		AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
		AllowedHeaders: []string{"*"},
		Debug:          true,
		//AllowCredentials: true,
	})
	app.Logger().Info("corsSetting...")
	/* 定义路由 */
	main = app.Party("/", crs).AllowMethods(iris.MethodOptions)
	main.Use(middleware.ServeHTTP)

	return main
}

type IrisConfig struct {
	DisablePathCorrection             bool   // `toml:"DisablePathCorrection"`
	EnablePathEscape                  bool   // `toml:"EnablePathEscape"`
	FireMethodNotAllowed              bool   // `toml:"FireMethodNotAllowed"`
	DisableBodyConsumptionOnUnmarshal bool   // `toml:"DisableBodyConsumptionOnUnmarshal"`
	TimeFormat                        string // `toml:"TimeFormat"`
	Charset                           string // `toml:"Charset"`
}

//[web.irisconfig]
//DisablePathCorrection = false
//EnablePathEscape = false
//FireMethodNotAllowed = false
//DisableBodyConsumptionOnUnmarshal = true
//# Mon, 01 Jan 2006 15:04:05 GMT
//TimeFormat = 2006-01-02T15:04:05
//Charset = UTF-8

func (conf *IrisConfig) ConvertIrisConfig() (iconf iris.Configuration) {
	iconf.Charset = conf.Charset
	iconf.TimeFormat = conf.TimeFormat
	iconf.DisableBodyConsumptionOnUnmarshal = conf.DisableBodyConsumptionOnUnmarshal
	iconf.FireMethodNotAllowed = conf.FireMethodNotAllowed
	iconf.EnablePathEscape = conf.EnablePathEscape
	iconf.DisablePathCorrection = conf.DisablePathCorrection
	return iconf
}

type Other struct {
	Port       int    // `toml:"Port"`
	IgnoreURLs string // `toml:"IgnoreURLs"`
	JWTTimeout int    // `toml:"JWTTimeout"`
	LogLevel   string // `toml:"LogLevel"`
	Secret     string // `toml:"Secret"`
}

func (o *Other) ConvertOtherToMap() (map[string]interface{}, error) {
	return utils.StructToMap(*o)
}
